package org.c41.util.log;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;

import org.c41.system.ImpossibleException;
import org.c41.util.StringHelper;

public final class Log {

	private static LogConfig one = new LogConfig();
	
	public static LogConfig getCurrentLogConfig(){
		return one;
	}
	
	private Log(){
		throw new ImpossibleException();
	}

	public static final int	FATAL = 2048;
	public static final int	ERROR = 1024;
	public static final int	WARN = 256;
	public static final int	INFO = 64;
	public static final int	DEBUG = 16;
	public static final int	TRACE = 1;

	static LogImpl logger = new LogOff();

	public static void fatal(String message, Object...objs){
		logger.fatal(message,objs);
	}

	public static void error(String message, Object...objs){
		logger.error(message,objs);
	}

	public static void warn(String message, Object...objs){
		logger.warn(message,objs);
	}

	public static void info(String message, Object... objs){
		logger.info(message,objs);
	}

	public static void debug(String message, Object... objs) {
		logger.debug(message, objs);
	}

	public static void trace(String message, Object... objs) {
		logger.trace(message,objs);
	}

	public static void log(int level, String message, Object... objs){
		logger.log(level,message,objs);
	}

}

interface LogImpl{

	public void fatal(String message, Object... objs);

	public void log(int level, String message, Object... objs);

	public void error(String message, Object... objs);

	public void debug(String message, Object... objs);

	public void warn(String message, Object... objs);

	public void info(String message, Object... objs);

	public void trace(String message, Object... objs);

}

class LogOff implements LogImpl{

	@Override
	public void debug(String message, Object... objs) {}

	@Override
	public void trace(String message, Object... objs) {}

	@Override
	public void info(String message, Object... objs) {}

	@Override
	public void warn(String message, Object... objs) {}

	@Override
	public void fatal(String message, Object... objs) {}

	@Override
	public void error(String message, Object... objs) {}

	@Override
	public void log(int level, String message, Object... objs) {}

}

class LogOn implements LogImpl{

	private final static int TRACE_INDEX = 4;

	private final LogConfig config = Log.getCurrentLogConfig();

	private  void m_log(String message, Object[] args, int level){
		long time = System.currentTimeMillis();
		StackTraceElement ele = Thread.currentThread().getStackTrace()[TRACE_INDEX];
		String cl = ele.getClassName();
		String method = ele.getMethodName();
		String file = ele.getFileName();
		int line = ele.getLineNumber();
		String thread = Thread.currentThread().getName();

		Object target = config.inner_getLogTarget(cl);
		LogMessage logger = config.inner_getLogMessage(cl);
		int loglevel = config.inner_getLogLevel(cl);
		if(loglevel>level){
			return;
		}

		String output = logger.debugMessage(translate(message, args),time,cl,method,file,line,thread, level);
		m_write_to_target(output, target);
	}

	private String translate(String format, Object... args){
		StringBuilder builder = new StringBuilder();
		String[] splits = StringHelper.split(format,"%");
		for(int i=0;i<splits.length-1;i++){
			builder.append(splits[i]); 
			if(i>=args.length){
				builder.append((String)null);
			}
			else{
				builder.append(args[i]);
			}
		}
		builder.append(splits[splits.length-1]);
		return builder.toString();
	}

	private void m_write_to_target(String message, Object target){
		if(target instanceof PrintStream){
			PrintStream s = (PrintStream)target;
			s.print(message);
		}
		else if(target instanceof Writer){
			Writer s = (Writer)target;
			try {
				s.write(message);
			} catch (IOException e) {
			}
		}
		else if(target instanceof File){
			File s = (File)target;
			try (FileWriter writer = new FileWriter(s, true);){
				writer.write(message);
			} catch (IOException e) {
			}
		}
	}

	@Override
	public void debug(String message, Object... objs){
		m_log(message, objs, Log.DEBUG);
	}

	@Override
	public void trace(String message, Object... objs) {
		m_log(message, objs, Log.TRACE);
	}

	@Override
	public void info(String message, Object... objs) {
		m_log(message, objs, Log.INFO);
	}

	@Override
	public void warn(String message, Object... objs) {
		m_log(message, objs, Log.WARN);
	}

	@Override
	public void fatal(String message, Object... objs) {
		m_log(message, objs, Log.FATAL);
	}

	@Override
	public void error(String message, Object... objs) {
		m_log(message, objs, Log.ERROR);
	}

	@Override
	public void log(int level, String message, Object... objs) {
		m_log(message, objs, level);
	}

}